#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@author:Zx
@file: auth.py
@time: 2025/7/23  10:43
# @describe: 认证
"""

from flask import Blueprint, render_template, request, redirect, url_for, flash, session, make_response, current_app
from flask_login import login_user, logout_user, current_user, login_required
from werkzeug.security import generate_password_hash, check_password_hash
from utils.data_utils import load_users, save_users, get_next_user_id
from models.user import User
from utils.image_utils import generate_captcha
import logging

import random
import string
from flask import jsonify
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
import threading
from datetime import datetime, timedelta, timezone

auth_bp = Blueprint('auth', __name__)


# 存储邮箱验证码的临时字典
email_verification_codes = {}


@auth_bp.route('/send-verification-email', methods=['POST'])
def send_verification_email():
    try:
        data = request.get_json()
        email = data.get('email')

        if not email:
            return jsonify({'success': False, 'message': '邮箱地址不能为空'})

        # 检查邮箱是否已被注册
        users = load_users()
        if any(u['email'] == email for u in users):
            return jsonify({'success': False, 'message': '该邮箱已被注册'})

        # 生成6位随机验证码
        code = ''.join(random.choices(string.digits, k=6))
        expiration = datetime.now() + timedelta(minutes=10)

        # 存储验证码
        email_verification_codes[email] = {
            'code': code,
            'expiration': expiration,
            'verified': False
        }

        # 获取当前应用的配置（在主线程中获取）
        mail_config = {
            'server': current_app.config['MAIL_SERVER'],
            'port': current_app.config['MAIL_PORT'],
            'username': current_app.config['MAIL_USERNAME'],
            'password': current_app.config['MAIL_PASSWORD'],
            'sender': current_app.config['MAIL_DEFAULT_SENDER'][1]
        }

        # 创建线程安全的邮件发送函数
        def send_email_thread(config, email, code):
            try:
                msg = MIMEText(f'您的验证码是: {code}\n验证码10分钟内有效。', 'plain', 'utf-8')
                msg['From'] = config['sender']
                msg['To'] = email
                msg['Subject'] = '徒步社区 - 邮箱验证码'

                with smtplib.SMTP_SSL(config['server'], config['port']) as server:
                    server.login(config['username'], config['password'])
                    server.sendmail(config['sender'], email, msg.as_string())
            except Exception as e:
                # 使用标准logging而不是current_app.logger
                logging.error(f'发送邮件失败: {str(e)}')

        # 启动线程发送邮件
        threading.Thread(
            target=send_email_thread,
            args=(mail_config, email, code)
        ).start()

        return jsonify({'success': True})
    except Exception as e:
        logging.error(f'发送验证邮件失败: {str(e)}')
        return jsonify({'success': False, 'message': '服务器错误'})


@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    try:
        if current_user.is_authenticated:
            return redirect(url_for('hikes.index'))

        if request.method == 'POST':
            username = request.form.get('username')
            password = request.form.get('password')
            remember = bool(request.form.get('remember'))

            users = load_users()
            user_data = next((u for u in users if u['username'] == username), None)

            if user_data and check_password_hash(user_data['password'], password):
                # 更新登录时间
                user_data['last_login_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                user_data['last_activity_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

                # 保存更新
                for i, u in enumerate(users):
                    if u['username'] == username:
                        users[i] = user_data
                        break
                save_users(users)

                # 登录用户
                user_obj = User(user_data)
                login_user(user_obj, remember=remember)

                flash('登录成功', 'success')
                return redirect(url_for('hikes.index'))
            else:
                flash('用户名或密码错误', 'error')

        return render_template('login.html')
    except Exception as e:
        logging.error(f'登录处理失败: {str(e)}', exc_info=True)
        flash('登录过程中发生错误', 'error')
        return render_template('login.html')


@auth_bp.route('/logout')
@login_required
def logout():
    try:
        logout_user()
        flash('您已成功登出', 'success')
        return redirect(url_for('hikes.index'))
    except Exception as e:
        logging.error(f'登出处理失败: {str(e)}', exc_info=True)
        flash('登出过程中发生错误', 'error')
        return redirect(url_for('hikes.index'))


# 修改注册路由，添加邮箱验证检查
@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
    try:
        if current_user.is_authenticated:
            return redirect(url_for('index'))

        if request.method == 'POST':
            # 验证表单数据
            user_captcha = request.form.get('captcha', '').upper()
            username = request.form.get('username')
            email = request.form.get('email')
            password = request.form.get('password')
            confirm_password = request.form.get('confirm_password')
            verification_code = request.form.get('email_verification_code')

            # 基本验证
            if not all([username, email, password, confirm_password, user_captcha]):
                flash('请填写所有字段')
                return redirect(url_for('auth.register'))

            if password != confirm_password:
                flash('两次输入的密码不一致')
                return redirect(url_for('auth.register'))

            # 验证码验证
            if 'captcha' not in session or user_captcha != session['captcha']['text']:
                flash('验证码错误')
                return redirect(url_for('auth.register'))

            session.pop('captcha', None)

            # 邮箱验证码验证
            if email not in email_verification_codes:
                flash('请先获取邮箱验证码')
                return redirect(url_for('auth.register'))

            verification_data = email_verification_codes[email]

            if datetime.now() > verification_data['expiration']:
                flash('验证码已过期，请重新获取')
                return redirect(url_for('auth.register'))

            if verification_code != verification_data['code']:
                flash('邮箱验证码错误')
                return redirect(url_for('auth.register'))

            users = load_users()

            # 检查用户名和邮箱是否已存在
            if any(u['username'] == username for u in users):
                flash('用户名已存在')
                return redirect(url_for('auth.register'))

            if any(u['email'] == email for u in users):
                flash('邮箱已被注册')
                return redirect(url_for('auth.register'))

            # 创建新用户
            new_user = {
                "id": get_next_user_id(),
                "username": username,
                "email": email,
                "password": generate_password_hash(password),
                # "registration_time": datetime.utcnow().isoformat() + 'Z',
                "registration_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                "last_login_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                "account_updated_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                "email_verified_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),  # 标记为已验证
                "last_activity_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                "role": "user",
                "data_add_records": {}
            }

            users.append(new_user)
            save_users(users)

            # 清除验证码记录
            email_verification_codes.pop(email, None)

            # 登录新用户
            user_obj = User(new_user)
            login_user(user_obj)

            flash('注册成功', 'success')
            return redirect(url_for('index'))

        return render_template('register.html')
    except Exception as e:
        logging.error(f'注册处理失败: {str(e)}', exc_info=True)
        flash('注册过程中发生错误', 'error')
        return render_template('register.html')


@auth_bp.route('/register/captcha')
def captcha():
    try:
        buf = generate_captcha()
        response = make_response(buf.read())
        response.headers['Content-Type'] = 'image/png'
        return response
    except Exception as e:
        logging.error(f'生成验证码失败: {str(e)}', exc_info=True)
        return make_response('验证码生成失败', 500)


@auth_bp.route('/forgot-password', methods=['GET', 'POST'])
def forgot_password():
    try:
        if request.method == 'POST':
            email = request.form.get('email')

            if not email:
                flash('请输入邮箱地址', 'error')
                return redirect(url_for('auth.forgot_password'))

            users = load_users()
            user_data = next((u for u in users if u['email'] == email), None)

            if not user_data:
                flash('该邮箱未注册', 'error')
                return redirect(url_for('auth.forgot_password'))

            # 生成重置令牌
            reset_token = ''.join(random.choices(string.ascii_letters + string.digits, k=32))

            # 使用本地时间（北京时间）并设置1小时有效期
            beijing_time = datetime.now(timezone(timedelta(hours=8)))  # UTC+8
            expiration = beijing_time + timedelta(hours=1)

            # 存储重置令牌和过期时间（使用ISO格式存储，包含时区信息）
            user_data['reset_token'] = reset_token
            user_data['reset_token_expiration'] = expiration.isoformat()

            # 更新用户数据
            for i, u in enumerate(users):
                if u['email'] == email:
                    users[i] = user_data
                    break
            save_users(users)

            # 发送重置邮件
            reset_link = url_for('auth.reset_password', token=reset_token, _external=True)

            # 获取邮件配置
            mail_config = {
                'server': current_app.config['MAIL_SERVER'],
                'port': current_app.config['MAIL_PORT'],
                'username': current_app.config['MAIL_USERNAME'],
                'password': current_app.config['MAIL_PASSWORD'],
                'sender': current_app.config['MAIL_DEFAULT_SENDER'][1]
            }

            def send_reset_email(config, email, reset_link):
                try:
                    msg = MIMEText(
                        f'请点击以下链接重置您的密码(1小时内有效):\n{reset_link}\n\n如果您没有请求重置密码，请忽略此邮件。',
                        'plain', 'utf-8'
                    )
                    msg['From'] = config['sender']
                    msg['To'] = email
                    msg['Subject'] = '徒步社区 - 密码重置'

                    with smtplib.SMTP_SSL(config['server'], config['port']) as server:
                        server.login(config['username'], config['password'])
                        server.sendmail(config['sender'], email, msg.as_string())
                except Exception as e:
                    logging.error(f'发送密码重置邮件失败: {str(e)}')

            # 异步发送邮件
            threading.Thread(
                target=send_reset_email,
                args=(mail_config, email, reset_link)
            ).start()

            flash('密码重置链接已发送到您的邮箱，请查收', 'success')
            return redirect(url_for('auth.login'))

        return render_template('forgot_password.html')
    except Exception as e:
        logging.error(f'忘记密码处理失败: {str(e)}', exc_info=True)
        flash('处理请求时发生错误', 'error')
        return redirect(url_for('auth.forgot_password'))


@auth_bp.route('/reset-password/<token>', methods=['GET', 'POST'])
def reset_password(token):
    try:
        users = load_users()
        user_data = next((u for u in users if u.get('reset_token') == token), None)

        if not user_data:
            flash('无效或过期的重置链接', 'error')
            return redirect(url_for('auth.forgot_password'))

        # 检查令牌是否过期
        # expiration = datetime.fromisoformat(user_data['reset_token_expiration'].replace('Z', ''))
        expiration = datetime.fromisoformat(user_data['reset_token_expiration'])
        current_time = datetime.now(expiration.tzinfo)  # 使用相同的时区

        # if datetime.utcnow() > expiration:
        if current_time > expiration:
            flash('重置链接已过期', 'error')
            return redirect(url_for('auth.forgot_password'))

        if request.method == 'POST':
            password = request.form.get('password')
            confirm_password = request.form.get('confirm_password')

            if not password or not confirm_password:
                flash('请填写所有字段', 'error')
                return redirect(url_for('auth.reset_password', token=token))

            if password != confirm_password:
                flash('两次输入的密码不一致', 'error')
                return redirect(url_for('auth.reset_password', token=token))

            # 更新密码
            user_data['password'] = generate_password_hash(password)
            user_data['account_updated_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

            # 清除重置令牌
            user_data.pop('reset_token', None)
            user_data.pop('reset_token_expiration', None)

            # 保存更新
            for i, u in enumerate(users):
                if u.get('reset_token') == token:
                    users[i] = user_data
                    break
            save_users(users)

            flash('密码重置成功，请使用新密码登录', 'success')
            return redirect(url_for('auth.login'))

        return render_template('reset_password.html', token=token)
    except Exception as e:
        logging.error(f'密码重置处理失败: {str(e)}', exc_info=True)
        flash('处理请求时发生错误', 'error')
        return redirect(url_for('auth.forgot_password'))